package com.lalilu.lmedia.extension

import android.content.Context
import android.media.MediaMetadataRetriever
import android.net.Uri
import com.blankj.utilcode.util.EncryptUtils
import com.blankj.utilcode.util.UriUtils
import com.lalilu.lmedia.entity.LSong
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jaudiotagger.audio.AudioFileIO
import org.jaudiotagger.tag.FieldKey
import java.io.File
import java.net.URLDecoder

internal interface Retriever : CoroutineScope {
    fun getSongById(mediaId: String): LSong?

    fun read(mContext: Context, uri: Uri?, callback: (LSong?) -> Unit) = launch {
        if (uri == null || !PermissionUtils.check(context = mContext)) {
            callback(null)
            return@launch
        }

        val mediaId = uri.extraMediaId(mContext)
        if (mediaId != null) {
            callback(getSongById(mediaId))
            return@launch
        }

        var file: File? = null
        try {
            file = UriUtils.uri2File(uri)

            var fileName = file.name
            var fileExt = file.extension

            if (file.extension.isEmpty()) {
                fileName = withContext(Dispatchers.IO) {
                    URLDecoder.decode(uri.toString(), "UTF-8")
                        .substringAfterLast('/')
                }
                fileExt = fileName.substringAfterLast('.')
            }

            var song = runCatching {
                val audioFile = AudioFileIO.readAs(file, fileExt)
                LSong(
                    id = EncryptUtils.encryptMD5ToString(uri.toString()),
                    name = audioFile.tag.getFirst(FieldKey.TITLE),
                    _artist = audioFile.tag.getFirst(FieldKey.ARTIST),
                    _albumTitle = audioFile.tag.getFirst(FieldKey.ALBUM),
                    _albumArtist = audioFile.tag.getFirst(FieldKey.ALBUM_ARTIST),
                    _albumId = "",
                    fileName = fileName,
                    pathStr = uri.toString(),
                    mimeType = audioFile.audioHeader.format,
                    durationMs = audioFile.audioHeader.trackLength * 1000L,
                    size = file.length(),
                    disc = audioFile.tag.getFirst(FieldKey.DISC_NO)?.toIntOrNull(),
                    track = audioFile.tag.getFirst(FieldKey.TRACK)?.toIntOrNull(),
                    uri = uri,
                    dateAdded = file.lastModified() / 1000,
                    dateModified = file.lastModified() / 1000,
                    sourceType = SourceType.Local
                )
            }.getOrNull()

            song = song ?: runCatching {
                val retriever = MediaMetadataRetriever()
                retriever.setDataSource(mContext, uri)

                LSong(
                    id = EncryptUtils.encryptMD5ToString(uri.toString()),
                    name = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE)
                        ?: fileName.substringBeforeLast('.'),
                    _artist = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST)
                        ?: "",
                    _albumTitle = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM)
                        ?: "",
                    _albumArtist = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST)
                        ?: "",
                    _albumId = "",
                    fileName = fileName,
                    pathStr = uri.toString(),
                    mimeType = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE)
                        ?: "",
                    durationMs = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
                        ?.toLongOrNull() ?: 0L,
                    size = file.length(),
                    disc = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER)
                        ?.toIntOrNull(),
                    track = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER)
                        ?.toIntOrNull(),
                    uri = uri,
                    dateAdded = System.currentTimeMillis(),
                    dateModified = System.currentTimeMillis(),
                    sourceType = SourceType.Local
                )
            }.getOrNull()

            callback(song)
        } catch (e: Exception) {
            callback(null)
        } finally {
            file?.delete()
        }
    }
}